/////////////////////////////////////////////////////////////////////////
//
// Amuse Engine SDK - core/math
// Copyright (c) 2014.  All Rights Reserved
//
// File:		AEVector2.h
// Author:		Gianluca Belardelli
// Date:		28/07/2013
// Purpose:		Implementazione dei metodi inline della classe AEVector2
//
// Revision:
//				001 - 28/07/2013
//					+ Revisione iniziale
//
/////////////////////////////////////////////////////////////////////////
#ifndef _AEVECTOR2_INL_
#define _AEVECTOR2_INL_

#include <core/math/vector/AEVector3.h>
#include <core/math/vector/AEVector4f.h>

// Helper macro to copy vector parameters into local variables
// This might look like stupid unnecessary code, but it helps the compiler to optimize the assembly code
#define AEVECTOR2_COPYTEMPS(var) \
  const float var##_x = var.fX; const float var##_y = var.fY;


AE_FORCEINLINE AEVector2::AEVector2( void ) { }
AE_FORCEINLINE AEVector2::AEVector2( float _fX, float _fY ) : fX( _fX ), fY( _fY ) { }
AE_FORCEINLINE AEVector2::AEVector2( float fXY ) : fX( fXY ), fY( fXY ) { }

AE_FORCEINLINE void AEVector2::SetZero( void )
{
	fX = 0.0f;
	fY = 0.0f;
}

AE_FORCEINLINE void AEVector2::Set( float _fX, float _fY )
{
	fX = _fX;
	fY = _fY;
}

AE_FORCEINLINE void AEVector2::Set( float fXY )
{
	fX = fXY;
	fY = fXY;
}

AE_FORCEINLINE float AEVector2::operator[] ( AEUINT32 uiIdx ) const
{
	AEASSERT( uiIdx <= 1 );
	return fData[uiIdx];
}

AE_FORCEINLINE float& AEVector2::operator[] ( AEUINT32 uiIdx )
{
	AEASSERT( uiIdx <= 1 );
	return fData[uiIdx];
}

AE_FORCEINLINE const AEVector2 AEVector2::operator- () const
{
	return AEVector2( -fX, -fY );
}

AE_FORCEINLINE void AEVector2::operator+= ( const AEVector2 &vcRhs )
{
	AEVECTOR2_COPYTEMPS( vcRhs );
	fX += vcRhs_x;
	fY += vcRhs_y;
}

AE_FORCEINLINE void AEVector2::operator-= ( const AEVector2 &vcRhs )
{
	AEVECTOR2_COPYTEMPS( vcRhs );
	fX -= vcRhs_x;
	fY -= vcRhs_y;
}

AE_FORCEINLINE void AEVector2::operator+= ( float fValue )
{
	fX += fValue;
	fY += fValue;
}

AE_FORCEINLINE void AEVector2::operator-= ( float fValue )
{
	fX -= fValue;
	fY -= fValue;
}

AE_FORCEINLINE void AEVector2::operator*= ( float fValue )
{
	fX *= fValue;
	fY *= fValue;
}

AE_FORCEINLINE void AEVector2::operator/= ( float fValue )
{
	const float fRes = 1.0f / fValue;

	fX *= fRes;
	fY *= fRes;
}

AE_FORCEINLINE bool AEVector2::IsZero( float fEpsilon ) const
{
	AEASSERT( fEpsilon >= 0.0f );

	if( fEpsilon == 0.0f )
		return( ( fX == 0 ) && ( fY == 0 ) );

	return ( ( fX >= -fEpsilon ) && ( fX <= fEpsilon ) &&
			 ( fY >= -fEpsilon ) && ( fY <= fEpsilon ) );
}

AE_FORCEINLINE bool AEVector2::IsValid( void ) const
{
	return( AEMath::IsFiniteNumber( fX ) && AEMath::IsFiniteNumber( fY ) );
}

AE_FORCEINLINE bool AEVector2::IsNormalized( float fEpsilon ) const
{
	AEASSERT( fEpsilon >= 0.0f );

	const float fLen = GetLengthSquared();

	return AEMath::IsFloatEqual( fLen, 1.0f, fEpsilon );
}

AE_FORCEINLINE bool AEVector2::IsIdentical( const AEVector2 &vcRhs ) const
{
	AEVECTOR2_COPYTEMPS( vcRhs );

	return ( ( fX == vcRhs_x ) && ( fY == vcRhs_y ) );
}

AE_FORCEINLINE bool AEVector2::IsEqual( const AEVector2 &vcRhs, float fEpsilon ) const
{
	AEASSERT( fEpsilon >= 0.0f );

	AEVECTOR2_COPYTEMPS( vcRhs );

	return ( AEMath::IsFloatEqual( fX, vcRhs_x, fEpsilon ) &&
			 AEMath::IsFloatEqual( fY, vcRhs_y, fEpsilon ) );
}

AE_FORCEINLINE float AEVector2::GetLengthSquared( void ) const
{
	return ( ( fX * fX ) + ( fY * fY ) );
}

AE_FORCEINLINE float AEVector2::GetLength( void ) const
{
	return AEMath::Sqrt( GetLengthSquared() );
}

AE_FORCEINLINE float AEVector2::GetLengthAndNormalize( void )
{
	const float fLength = GetLength();
	operator*= ( 1.0f / fLength );

	return fLength;
}

AE_FORCEINLINE float AEVector2::GetLengthInverse( void ) const
{
	return AEMath::SqrtInverse( GetLengthSquared() );
}

AE_FORCEINLINE const AEVector2 AEVector2::GetNormalized( void ) const
{
	AEVector2 vcTemp( *this );
	vcTemp.Normalize();
	return vcTemp;
}

AE_FORCEINLINE void AEVector2::Normalize( void )
{
	operator*= ( GetLengthInverse() );
}

AE_FORCEINLINE AERESULT AEVector2::NormalizeIfNotZero( float fEpsilon )
{
	if( ( IsZero( fEpsilon )) || ( !IsValid() ) )
		return AE_FAILURE;

	Normalize();

	return AE_SUCCESS;
}

AE_FORCEINLINE float AEVector2::NormalizedEnsureUnitLength( void )
{
	if( ( IsZero( AEMATH_EPSILON ) ) || ( !IsValid() ) )
	{
		Set( 1, 0 );
		return 0.0f;
	}

	const float fLen = GetLength();
	*this /= fLen;

	return fLen;
}

AE_FORCEINLINE float AEVector2::GetDistanceTo( const AEVector2 &vcRhs ) const
{
	return ( vcRhs - *this ).GetLength();
}

AE_FORCEINLINE float AEVector2::GetDistanceToSquared( const AEVector2 &vcRhs ) const
{
	return ( vcRhs - *this ).GetLengthSquared();
}

AE_FORCEINLINE AERESULT AEVector2::SetLength( float fLen, float fEpsilon )
{
	const AERESULT hRes = NormalizeIfNotZero( fEpsilon );
	operator*= ( fLen );

	return hRes;
}

AE_FORCEINLINE void AEVector2::SetMin( const AEVector2 &vcRhs )
{
	SetMin( *this, vcRhs );
}

AE_FORCEINLINE void AEVector2::SetMax( const AEVector2 &vcRhs )
{
	SetMax( *this, vcRhs );
}

AE_FORCEINLINE void AEVector2::ClampTo( const AEVector2 &vcMinVal, const AEVector2 &vcMaxVal )
{
	SetClamped( *this, vcMinVal, vcMaxVal );
}

AE_FORCEINLINE const AEVector2 AEVector2::CompMul( const AEVector2 &vcRhs ) const
{
	AEVECTOR2_COPYTEMPS( vcRhs );

	return AEVector2( fX * vcRhs_x, fY * vcRhs_y );
}

AE_FORCEINLINE const AEVector2 AEVector2::CompDiv( const AEVector2 &vcRhs ) const
{
	AEVECTOR2_COPYTEMPS( vcRhs );

	return AEVector2( fX / vcRhs_x, fY / vcRhs_y );
}

AE_FORCEINLINE void AEVector2::SetMin( const AEVector2 &vcLhs, const AEVector2 &vcRhs )
{
	AEVECTOR2_COPYTEMPS( vcLhs );
	AEVECTOR2_COPYTEMPS( vcRhs );

	fX = AEMath::Min( vcLhs_x, vcRhs_x );
	fY = AEMath::Min( vcLhs_y, vcRhs_y );
}

AE_FORCEINLINE void AEVector2::SetMax( const AEVector2 &vcLhs, const AEVector2 &vcRhs )
{
	AEVECTOR2_COPYTEMPS( vcLhs );
	AEVECTOR2_COPYTEMPS( vcRhs );

	fX = AEMath::Max( vcLhs_x, vcRhs_x );
	fY = AEMath::Max( vcLhs_y, vcRhs_y );
}

AE_FORCEINLINE void AEVector2::SetClamped( const AEVector2 &vcVal, const AEVector2 &vcMinVal, const AEVector2 &vcMaxVal )
{
	AEVECTOR2_COPYTEMPS( vcVal );
	AEVECTOR2_COPYTEMPS( vcMinVal );
	AEVECTOR2_COPYTEMPS( vcMaxVal );

	fX = AEMath::Clamp( vcVal_x, vcMinVal_x, vcMaxVal_x );
	fY = AEMath::Clamp( vcVal_y, vcMinVal_y, vcMaxVal_y );
}

AE_FORCEINLINE void AEVector2::SetAbs( const AEVector2 &vcVal )
{
	AEVECTOR2_COPYTEMPS( vcVal );

	fX = AEMath::Abs( vcVal_x );
	fY = AEMath::Abs( vcVal_y );
}

AE_FORCEINLINE void AEVector2::SetInterpolate( const AEVector2 &vcLhs, const AEVector2 &vcRhs, float fFactor )
{
	AEVECTOR2_COPYTEMPS( vcLhs );
	AEVECTOR2_COPYTEMPS( vcRhs );

	fX = AEMath::Interpolate( vcLhs_x, vcRhs_x, fFactor );
	fY = AEMath::Interpolate( vcLhs_y, vcRhs_y, fFactor );
}

AE_FORCEINLINE float AEVector2::Dot( const AEVector2 &vcRhs ) const
{
	AEVECTOR2_COPYTEMPS( vcRhs );

	return ( ( fX * vcRhs_x ) + ( fY * vcRhs_y ) );
}

AE_FORCEINLINE void AEVector2::MakeOrthogonalTo( const AEVector2 &vcNormal )
{
	AEASSERT_MSG( vcNormal.IsNormalized(), "The normal must be normalized." );

	// get the distance v is along this vector
	const float fDot = this->Dot( vcNormal );

	//subtract the distance to make it orthogonal
	*this -= fDot * vcNormal;
}

AE_FORCEINLINE const AEVector2 AEVector2::GetOrthogonalVector( void ) const
{
	return AEVector2( fY, -fX );
}

AE_FORCEINLINE void AEVector2::Negate( void )
{
	fX = -fX;
	fY = -fY;
}

AE_FORCEINLINE void AEVector2::Reflect( const AEVector2 &vcNormal )
{
	*this = GetReflected( vcNormal );
}

AE_FORCEINLINE const AEVector2 AEVector2::GetReflected( const AEVector2 &vcNormal ) const
{
	AEASSERT_MSG( vcNormal.IsNormalized(), "The normal must be normalized." );

	const float fDot = this->Dot( vcNormal );

	return ( *this ) - 2.0f * fDot * vcNormal;
}

AE_FORCEINLINE const AEVector3 AEVector2::GetAsVector3( float fZ ) const
{
	return AEVector3( fX, fY, fZ );
}

AE_FORCEINLINE const AEVector4f AEVector2::GetAsVector4( float fZ, float fW ) const
{
	return AEVector4f( fX, fY, fZ, fW );
}

AE_FORCEINLINE bool operator== ( const AEVector2 &vcLhs, const AEVector2 &vcRhs )
{
	return vcLhs.IsIdentical( vcRhs );
}

AE_FORCEINLINE bool operator!= ( const AEVector2 &vcLhs, const AEVector2 &vcRhs )
{
	return !vcLhs.IsIdentical( vcRhs );
}

AE_FORCEINLINE const AEVector2 operator+ ( const AEVector2 &vcLhs, const AEVector2 &vcRhs )
{
	AEVector2 vcTemp( vcLhs );
	vcTemp += vcRhs;
	return vcTemp;
}

AE_FORCEINLINE const AEVector2 operator- ( const AEVector2 &vcLhs, const AEVector2 &vcRhs )
{
	AEVector2 vcTemp( vcLhs );
	vcTemp -= vcRhs;
	return vcTemp;
}

AE_FORCEINLINE const AEVector2 operator* ( const AEVector2 &vcLhs, float fValue )
{
	AEVector2 vcTemp( vcLhs );
	vcTemp *= fValue;
	return vcTemp;
}

AE_FORCEINLINE const AEVector2 operator* (float fValue, const AEVector2 &vcRhs )
{
	AEVector2 vcTemp( vcRhs );
	vcTemp *= fValue;
	return vcTemp;
}

AE_FORCEINLINE const AEVector2 operator/ ( const AEVector2 &vcLhs, float fValue )
{
	AEVector2 vcTemp( vcLhs );
	vcTemp /= fValue;
	return vcTemp;
}

#endif
// _AEVECTOR2_INL_